﻿#define TODO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Diagnostics;

using common;


namespace server
{
    /// <summary>
    /// Egy hálózati klienst ilyennel kezelünk
    /// </summary>
    public class KliensKezelo
    {
        #region Variables
        // ez azonosítja a klienst
        private KliensID _kliensID;

        private TcpClient _tcpClient;

        #endregion

        #region Constructors
        /// <summary>
        /// Konstruktor, TCP kliens megadással
        /// </summary>
        /// <param name="tcpCon">a TCP listener Accept-től kapja</param>
        public KliensKezelo(TcpClient tcpCon)
        {
            Log.logToFile("----KliensKezelo constructor----", true);

            _tcpClient = tcpCon;

            Thread SclientThread = null;

            SclientThread = new Thread(new ThreadStart(handleComm));
            SclientThread.IsBackground = true;
            SclientThread.Start();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Kell tudnunk, melyik klienst kezeli ez a KliensKezelo
        /// </summary>
        public KliensID kliensID
        {
            get
            {
                return _kliensID;
            }
        }
        #endregion

        #region Methods
        
        /// <summary>
        /// a hálózatról bejövő adatot itt kezeljük
        /// </summary>
        /// <param name="nm">a hálózatról bejövő üzenet</param>
        private void erkezettadatfeldolgozas(NetworkMessage nm)
        {
            //Itt kell érdemben foglalkozni a kliens felől érkező adattal
            MemoryStream ms = new MemoryStream(nm.objData);
            BinaryFormatter bf = new BinaryFormatter();
            object o = (object)bf.Deserialize(ms);
            ms.Close();

            //Console.WriteLine("incoming object: " + nm.objTipus);

            //most még csak stringre reagálunk...
            if (nm.objTipus == typeof(String))
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("Üzenet klienstől: {0} {1} {2} ", _kliensID.ip, _kliensID.id, (string)o);

                Console.WriteLine(sb.ToString());
            }

            if (nm.objTipus == typeof(KliensID))
            {
                if (!(Szerver.instance.loginElbiralas((KliensID)o)))
                {
                    Console.WriteLine("login failed... {0}", _kliensID.id);
                    this.sendObject("auth_failed");
                    return;
                }
                else
                {
                    this.sendObject("auth_oké");
                }
            }
        }

        /// <summary>
        /// a hálózatot itt kezeljuk, szálban indítva (vegtelen ciklus)
        /// </summary>
        private void handleComm()
        {

            MemoryStream ms;
            BinaryFormatter bf;
            NetworkStream clientStream = _tcpClient.GetStream();
            IFormatter formatter = new BinaryFormatter();

            // a halozatrol csak altalunk definialt adat johet:
            NetworkMessage networkmessage;

            try
            {
                //itt még nem tökéletes a hibakezelés
                networkmessage = (NetworkMessage)formatter.Deserialize(clientStream);
            }
            catch
            {
                Console.WriteLine("Nem Common.NetworkMessage üzenet jött...");
                return;
            }

            //Console.WriteLine("incoming object: " + networkmessage.objtipus);

            ms = new MemoryStream(networkmessage.objData);
            bf = new BinaryFormatter();

            //elso uzenet: logon object
            //ebbol elbiraljuk, van-e joga a kliensnek belepni

            try
            {
                _kliensID = (KliensID)bf.Deserialize(ms);
            }
            catch
            {
                Console.WriteLine("Az elso uzenet az uj klienstol nem logon, hanem valami mas...");
                _tcpClient.Close();
                return;
            }

            ms.Close();

            Szerver.instance.addKliensToKliensek(this);

            //es az ID-t is elküldjük
            sendObject(_kliensID);

            //Console.WriteLine("logon rendben: " + _kliensID.gepnev);

            //figyeljük a socketet
            while (true)
            {
                try
                {
                    networkmessage = (common.NetworkMessage)formatter.Deserialize(clientStream);

                    //ami jött, azt átadjuk feldolgozásra
                    erkezettadatfeldolgozas(networkmessage);
                }
                catch
                {
                    Log.logToFile("Disconnected: " + _kliensID.id.ToString(),false);

                    _tcpClient.Close();
                    _tcpClient = null;
                    Szerver.instance.kliensek.Remove(_kliensID.id);
                    Szerver.instance.kliensSzamlalo--;
                    
                    Debug.Assert(Szerver.instance.kliensSzamlalo >= 0, "A kliensszámlálóval baj van!", "Az érték nulla alá ment");
                    break;
                }
            }
        }

        /// <summary>
        /// feladunk hálózatra adatot
        /// </summary>
        /// <param name="o">az adat</param>
        public void sendObject(object o)
        {
            Log.logToFile("outging object: " + o.GetType().ToString(),false);
            
            NetworkStream clientStream = _tcpClient.GetStream();
            if (clientStream == null)
            {
                Console.WriteLine("nincs írható network stream...");
                return;
            }

            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter();

            bf.Serialize(ms, o);
            byte[] data = ms.GetBuffer();
            ms.Close();

            common.NetworkMessage üzenet = new common.NetworkMessage(o.GetType(), data);
            IFormatter formatter = new BinaryFormatter();
            //feladjuk a Stream-re
            formatter.Serialize(clientStream, üzenet);
        }

        /// <summary>
        /// lezárjuk a hálózati kapcsolatot
        /// </summary>
        public void closeConnection()
        {
            // Close KliensKapcsolat
            try
            {
                _tcpClient.Close();
            }
            catch
            {
                Console.WriteLine("Close KliensKapcsolat error...");
            }
        }
        #endregion
    }
}
